package com.gitee.feizns.quickstart.code.generator;

import com.gitee.feizns.dynamic.Strings;
import com.gitee.feizns.dynamic.bean.Prop;
import com.gitee.feizns.dynamic.reflect.Classes;
import com.gitee.feizns.dynamic.reflect.Fields;
import com.gitee.feizns.quickstart.ddl.annotation.Comment;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Accessors;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Pojo 元数据
 * @author feizns
 * @since 2024/11/29
 */
@Getter
@Accessors(chain = true)
class PojoMetadata {

    /**
     * POJO 类
     */
    private final Class<?> pojo;

    /**
     * 备注
     */
    private final String comment;

    /**
     * 主键
     */
    private final FieldMetadata id;

    /**
     * 字段列表非ID
     */
    private final List<FieldMetadata> fields;

    /**
     * Pojo 元数据
     * @param pojo POJO
     */
    public PojoMetadata(Class<?> pojo) {
        this(pojo, "id");
    }

    /**
     * Pojo 元数据
     * @param pojo POJO 类
     */
    public PojoMetadata(Class<?> pojo, String idName) {
        this.pojo = pojo;
        Field id = Fields.of(pojo, idName);
        if ( id == null )
            throw new RuntimeException(String.format("找不见主键字段[%s].", idName));
        //备注
        this.comment = pojo.isAnnotationPresent(Comment.class) ? pojo.getAnnotation(Comment.class).value() : null;
        //主键
        this.id = new FieldMetadata(id);
        //非主键字段列表
//        this.fields = Props.of().ofWritable(pojo).filter(field -> !field.name().equals(idName)).map(Prop::field).map(FieldMetadata::new).toList();
        this.fields = Arrays.stream(pojo.getDeclaredFields())
                .filter(field -> !field.getName().equals(idName) && Prop.of(pojo, field.getName()).isWritable())
                .map(FieldMetadata::new).collect(Collectors.toList());
    }

    /**
     * 获取名称
     * @return {@link String}
     */
    public String getName() {
        return pojo.getName();
    }

    /**
     * 获取简单名称
     * @return {@link String}
     */
    public String getSimpleName() {
        return pojo.getSimpleName();
    }

    /**
     * 连字符名称
     * @return {@link String}
     */
    public String getKebabCaseName() {
        return Strings.toKebabCase(pojo.getSimpleName());
    }

    /**
     * 字段类型列表
     * @return {@link Set}<{@link String}>
     */
    public String getFieldClassesForImport() {
        Set<String> types = id.types();
        for (FieldMetadata field : fields)
            types.addAll(field.types());
        return types.stream()
                .filter(type -> !type.startsWith("java.lang"))
                .map(type -> "import " + type + ";")
                .collect(Collectors.joining("\n"));
    }

    /**
     * 获取用于导入字段验证注释
     * @return {@link String }
     */
    public String getFieldValidateAnnotationForImport() {
        Set<String> content = new LinkedHashSet<>();
        for (FieldMetadata field : fields) {
            if ( field.getIsString() )
                content.add("import javax.validation.constraints.NotBlank;");
            if ( field.getIsCollectionOrArray() )
                content.add("import javax.validation.constraints.NotEmpty;");
            if ( field.getIsNormal() )
                content.add("import javax.validation.constraints.NotNull;");
        }
        return String.join("\n", content);
    }

    /**
     * 获取名称字段
     * @return {@link String }
     */
    public FieldMetadata getNameField() {
        return fields.stream().filter(field -> field.getName().equals("name")).findFirst().orElse(null);
    }

    /**
     * 字段元数据
     * @author feizns
     * @since 2024/11/29
     */
    @Getter
    @RequiredArgsConstructor
    public static class FieldMetadata {

        /**
         *
         */
        private final Field field;

        /**
         * 获取是否是字符串
         * @return {@link Boolean}
         */
        public Boolean getIsString() {
            return field.getType() == String.class;
        }

        /**
         * 非字符和集合
         * @return {@link Boolean}
         */
        public Boolean getIsNormal() {
            return !getIsString() && !getIsCollectionOrArray();
        }

        /**
         * 获取是否是收集
         * @return {@link Boolean}
         */
        public Boolean getIsCollectionOrArray() {
            return Collection.class.isAssignableFrom(field.getType()) || field.getType().isArray();
        }

        /**
         * 获取名称
         * @return {@link String}
         */
        public String getName() {
            return field.getName();
        }

        /**
         * 获取评论
         * @return {@link String}
         */
        public String getComment() {
            Comment annotation = field.getAnnotation(Comment.class);
            return annotation != null ? annotation.value(): null;
        }

        /**
         * 获取类型
         * @return {@link String}
         */
        public String getType() {
            String typeName = field.getGenericType().getTypeName();
            Set<String> packages = packages();
            for (String pack : packages)
                typeName = typeName.replace(pack + ".", "");
            return typeName.replace("$", ".");
        }

        /**
         * 类型
         * @return {@link Set}<{@link String}>
         */
        public Set<String> types() {
            Set<String> types = Arrays.stream(Classes.ofGenericTypes(field.getGenericType()))
                    .map(Class::getName)
                    .map(FieldMetadata::classname)
                    .collect(Collectors.toSet());
            types.add(classname(field.getType().getName()));
            return types;
        }

        /**
         * 字段所使用到的包列表
         * @return {@link Set}<{@link String}>
         */
        public Set<String> packages() {
            return types().stream().map(type -> type.substring(0, type.lastIndexOf("."))).collect(Collectors.toSet());
        }

        /**
         * 内部类名
         * @param classname 类名
         * @return {@link String}
         */
        private static String classname(String classname) {
            int index = classname.lastIndexOf("$");
            return index != -1 ? classname.substring(0, index): classname;
        }

    }

}
